#!/usr/bin/env python
# coding=utf-8
# CopyRight(c) 2019 Inspur Electronic Information Industry Co.,Ltd

"""
    secchecksuite/plugin.py
    测评工具套件插件解析执行函数
    :CopyRight(c) 2019 Inspur Electronic Information Industry Co.,Ltd
"""
import os
import sys
import json
import collections
import datetime
import export
import constant as const
import sqlitedb
import aes_cbc
import seccheck_log
import utils
import check


def parse_plugins(module, config_info, conn, key):
    """
    get entrypoint path and parse entrypoint info
    Raises:
        BaseException:  An error occurred creating a table
    """
    try:
        seccheck_log.sec_check_logger.info('Parse plugins begin!')
        plugin_module = module.get('entry')
        plugin_module = plugin_module.lower() if plugin_module else None
        entry_points = config_info.get('entrypoints', None)
        flag = False
        for entry in entry_points:
            if plugin_module and plugin_module != 'all' and plugin_module != entry:
                continue
            flag = True
            module_list = entry_points.get(entry, None)
            for plugin in module_list:
                plugin_list = plugin.get('plugin', None)
                if plugin_list is not None and plugin_list != '':
                    parse_entry_point_file(module, plugin_list, conn, key)
        if not flag:
            seccheck_log.sec_check_logger.info(
                'module {} is error: module should be [host, docker, k8s, third, extend]')
            utils.program_exit(sys.argv[0], -1)
        seccheck_log.sec_check_logger.info('Parse plugins succeeded!')
    except BaseException as e:
        seccheck_log.sec_check_logger.error('parse_plugins occur some erros:{}'.format(e))


def parse_entry_point_file(module, enter_point, conn, key):
    """
    parse entrypoint file
    Args:
        module: excute module
        enter_point: entry_point file path
        conn: sqlite database connect object
        key: key seed for generating secret key
    Raises:
        aseException:  An error occurred creating a table
    """
    try:
        seccheck_log.sec_check_logger.info('Parse entrypoint file begin!')
        plugins_data = utils.load_config_file(enter_point)
        module_id = module.get('module', None)
        confirm_id = plugins_data.get('id', None)
        if module_id and module_id != 'all' and module_id not in confirm_id:
            return
            # in the file of enter_point, param ['plugins'] must be a list
        plugins = plugins_data.get('plugins', None)
        if not isinstance(plugins, list) or len(plugins) == 0:
            seccheck_log.sec_check_logger.error('entrypoint:{} not include plugins or plugins not a list, '
                                                'continue next entrypoint'.format(enter_point))
            return
        plugin_type = plugins_data.get('plugintype', None)
        check.check_plugin_type(plugin_type)
        if plugin_type.upper() == const.PLUGIN_TYPE[0]:
            get_plugin_info(plugins_data, enter_point, conn, key)
        else:
            parse_plugin_info(plugins_data, enter_point, conn, key)
    except (ValueError, IOError, KeyError) as e:
        des = 'parse_entrypoint_file occur some error:{}.continue next plugin'.format(e)
        seccheck_log.sec_check_logger.error(des)


def get_plugin_info(plugins_data, enter_point, conn, key):
    """
    get entry_point path and parse entry_point info
    Args:
        plugins_data: json string about configure info
        enter_point: entry_point file path
        conn: sqlite database connect object
        key: key seed for generating secret key
    Raises:
        BaseException:  An error occurred creating a table
    """
    if not check.check_not_none_dict(plugins_data):
        return

    plugin_type = utils.get_plugin_type(plugins_data.get('plugintype', None))
    for plugin_info in plugins_data.get('plugins', None):
        try:
            if not plugin_info:
                seccheck_log.sec_check_logger.info('enter_point:{} plugin config is None'.format(plugin_info))
                continue
            plugins_list = {}
            plugin_exe = plugin_info.get('plugin_exe')
            seccheck_log.sec_check_logger.info('get plugin: {} info'.format(plugin_exe))
            check.check_file_exist(plugin_exe)
            res = os.popen('sha256sum %s' % plugin_exe)
            hash_value = res.read()
            hash_value_encrypt = aes_cbc.aes_encrypt(key, hash_value, out=False)
            hash_value_encrypt = hash_value_encrypt.decode(encoding='utf-8')
            time_now = datetime.datetime.now()
            end_time = time_now + datetime.timedelta(hours=2)
            result = plugin_type.createPlugin(key).get_plugin_info(plugin_info)
            check.check_info_none('{} get_plugin_info\'result'.format(plugin_exe), result)

            plugins_list['plugin_exe'] = plugin_exe
            plugins_list['scene'] = plugins_data.get('scene', None)
            plugins_list['source'] = plugins_data.get('source', None)
            plugins_list['plugintype'] = plugins_data.get('plugintype', None)
            plugins_list['hashvalue'] = hash_value
            plugins_list['hashvalue_encrypt'] = hash_value_encrypt
            plugins_list['timenow'] = time_now
            plugins_list['endtime'] = end_time
            plugins_list['enter_point_name'] = enter_point
            plugins_list['plugin_id'] = result.get('plugin_id', None)
            plugins_list['plugin_name'] = result.get('name', None)
            plugins_list['user_info'] = plugins_data.get('user_info', None)
            plugin_config = plugin_info.get('plugin_config', None)
            if isinstance(plugin_config, dict):
                plugins_list['plugin_config'] = json.dumps(plugin_config)
            else:
                plugins_list['plugin_config'] = plugin_config
            user_info = plugins_data.get('user_info', None)
            plugins_list['user_info'] = json.dumps(user_info)
            plugins_list['encryption'] = user_info.get('encryption', '0')
            plugins_list['stop_flag'] = plugin_info.get('stop_flag', '0')
            plugins_list['plugin_level'] = result.get('level', 'C')
            plugins_list['plugin_timeout'] = plugin_info.get('plugin_timeout', const.TIMEOUT)
            plugins_list['version'] = plugin_info.get('version', None)
            check.check_info_none('{}\'version'.format(plugin_exe), plugin_info.get('version', None))
            plugins_list['rollbackcmd'] = plugin_info.get('rollbackcmd', None)
            sqlitedb.insert_into_database(plugins_list, conn, key)
            seccheck_log.sec_check_logger.info(
                'get plugin: {} info and insert it into database succeeded'.format(plugin_exe))
        except (BaseException, KeyError) as e:
            seccheck_log.sec_check_logger.error(
                'get_plugin_info {} occur some error:{}'.format(plugin_info.get('plugin_exe'), e))


def parse_plugin_info(plugins_data, enter_point, conn, key):
    """
    parse entrypoint file and insert message to database
    Args:
        plugins_data: json data
        enter_point: entry_point file path
        conn: sqlite database connect object
        key: key seed for generating secret key
    Returns:
        plugins_lists:current entry_point all info
    Raises:
        BaseException:  An error occurred creating a table
    """
    if not check.check_not_none_dict(plugins_data):
        return None
    plugins_lists = []
    plugin_type = plugins_data.get('plugintype', None)
    scene = plugins_data.get('scene', None)
    source = plugins_data.get('source', None)
    for plugin_info in plugins_data.get('plugins', None):
        try:
            if not plugin_info:
                seccheck_log.sec_check_logger.info('enter_point:{} plugin config is None'.format(plugin_info))
                continue
            plugins_list = {}
            plugin_exe = plugin_info.get('plugin_exe')
            check.check_file_exist(plugin_exe)
            seccheck_log.sec_check_logger.info('when parse plugin_exe:{}'.format(plugin_exe))
            res = os.popen('sha256sum %s' % plugin_exe)
            hash_value = res.read()
            hash_value_encrypt = aes_cbc.aes_encrypt(key, hash_value, out=False)
            hash_value_encrypt = hash_value_encrypt.decode(encoding='utf-8')
            time_now = datetime.datetime.now()
            end_time = time_now + datetime.timedelta(hours=2)

            plugins_list['scene'] = scene
            plugins_list['source'] = source
            plugins_list['plugintype'] = plugin_type
            plugins_list['plugin_exe'] = plugin_exe
            plugins_list['hashvalue'] = hash_value
            plugins_list['hashvalue_encrypt'] = hash_value_encrypt
            plugins_list['timenow'] = time_now
            plugins_list['endtime'] = end_time
            plugins_list['enter_point_name'] = enter_point
            plugins_list['plugin_id'] = plugin_info.get('plugin_id')
            plugins_list['plugin_name'] = plugin_info.get('plugin_name', None)
            plugins_list['plugin_config'] = plugin_info.get('plugin_config', None)
            plugins_list['encryption'] = plugin_info.get('encryption', '0')
            plugins_list['plugin_level'] = plugin_info.get('plugin_level', 'C')
            plugins_list['stop_flag'] = plugin_info.get('stop_flag', '0')
            plugins_list['plugin_timeout'] = plugin_info.get('plugin_timeout', const.TIMEOUT)
            plugins_list['version'] = plugin_info.get('version', None)
            plugins_list['rollbackcmd'] = plugin_info.get('rollbackcmd', None)

            sqlitedb.insert_into_database(plugins_list, conn, key)
            plugins_lists.append(plugins_list)
            seccheck_log.sec_check_logger.info(
                'parse plugin: {} info and insert it into database succeeded'.format(plugin_exe))
        except (BaseException, KeyError) as e:
            seccheck_log.sec_check_logger.error(
                'parse plugin {} occur some error:{}'.format(plugin_info.get('plugin_exe'), e))
    return plugins_lists


def rollback_all(config_info, conn, key):
    """
    rollback exec program
    Raises:
        BaseException:  An error occurred cleanup a table
    """
    try:
        # 获取数据库中的信息
        seccheck_log.sec_check_logger.info('Roll back plugins begin!')
        plugin_lists = sqlitedb.getinfo_from_database(conn)
        check.check_info_none('rollback data in database', plugin_lists)

        # 翻转list，以逆序的方式回退所有插件
        plugin_lists = utils.reverse_list(plugin_lists)
        dict_rows = [collections.OrderedDict(row) for row in plugin_lists]

        # 回退所有插件
        for plugin in dict_rows:
            try:
                # level 比对
                check.check_level(plugin, config_info)
                # hash比对
                check.check_hash_value(plugin, key)
                # 判断插件类型
                plugin_type = utils.get_plugin_type(plugin['plugintype'])
                if plugin['plugintype'] == 'script':
                    rollback_cmd = json.loads(plugin['plugin_config'])
                    rollback_cmd['command'] = const.COMMAND
                    plugin['plugin_config'] = json.dumps(rollback_cmd)
                else:
                    # 参数解密
                    aes_cbc.decrypt_plugin_config(plugin, key)
                    plugin['plugin_config'] = plugin['plugin_config'] + const.SCROLL_CMD

                result = plugin_type.createPlugin(key).start(plugin)
                seccheck_log.sec_check_logger.info('roll back plugin {} succeeded.'.format(plugin['plugin_exe']))
                export.output_plugin_result(plugin, result)
                seccheck_log.sec_check_logger.info(
                    'output roll back plugin result {} succeeded.'.format(plugin['plugin_exe']))
            except BaseException as e:
                result = {"code": 1, "count": 0, "des": ['%s' % e]}
                export.output_plugin_result(plugin, result)
                seccheck_log.sec_check_logger.error('rollback_all occur some errors:{} '.format(e))

    except BaseException as e:
        seccheck_log.sec_check_logger.error('rollback_all occur some errors:{}'.format(e))
    finally:
        seccheck_log.sec_check_logger.info('rollback_all end!')


def exec_plugins(config_info, conn, key):
    """
    exec plugins
    Raises:
        BaseException:  An error occurred cleanup a table
    """
    try:
        seccheck_log.sec_check_logger.info('Execute plugins begin!')
        # 获取数据库中的信息
        plugin_lists = sqlitedb.getinfo_from_database(conn)
        check.check_info_none('exec_plugins data in database', plugin_lists)

        dict_rows = [dict(row) for row in plugin_lists]
        for plugin in dict_rows:
            try:
                # level 比对
                check.check_level(plugin, config_info)

                # hash 比对
                check.check_hash_value(plugin, key)

                # 本地执行or远程执行
                # scene = operation_environment(plugin)
                # scene.start(plugin)
                # 判断插件类型
                plugin_type = utils.get_plugin_type(plugin['plugintype'])
                if plugin['plugintype'].upper() == const.PLUGIN_TYPE[1]:
                    # decrypt plugin config parameter
                    aes_cbc.decrypt_plugin_config(plugin, key)
                result = plugin_type.createPlugin(key).start(plugin)
                seccheck_log.sec_check_logger.info('plugin {} execute succeeded'.format(plugin.get('plugin_exe')))

                # 导出结果
                export.output_plugin_result(plugin, result)
                seccheck_log.sec_check_logger.info(
                    'plugin {} execute result output succeeded'.format(plugin.get('plugin_exe')))

                # check stop_flag
                code = check.check_stop_flag(plugin, result)
                if code == 0:
                    seccheck_log.sec_check_logger.error(
                        'since plugin {} begin, all plugins later break off.'.format(plugin.get('plugin_exe')))
                    break

            except BaseException as e:
                err = '%s' % e
                result = {"code": 2, "count": 0, "des": err}
                export.output_plugin_result(plugin, result)

    except KeyError as e:
        seccheck_log.sec_check_logger.info('{}'.format(e))
    finally:
        seccheck_log.sec_check_logger.info('Execute plugins end!')
